package be.android.forap.gui;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.NoSuchProviderException;
import java.security.SignatureException;

import org.bouncycastle.openpgp.PGPException;

import be.android.forap.ForAp;
import be.android.forap.R;
import be.android.forap.crypto.PGPDigitalSignature;
import be.android.forap.crypto.PGPEncryption;
import be.android.forap.crypto.PatternUtil;
import be.android.forap.crypto.CryptoUtil;
import be.android.forap.dataprovider.ContactIntegrityElement;
import be.android.forap.dataprovider.ContactManager;
import be.android.forap.dataprovider.ContactsIntegrityDb;
import be.android.forap.dataprovider.ForapAdapter;
import be.android.forap.dataprovider.RawContact;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.text.ClipboardManager;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ViewFlipper;

public class DecryptActivity extends BaseActivity{

	private TextView selected;

	private ViewFlipper viewflipper;
	private Animation animFlipInNext;
	private Animation animFlipOutNext;
	private Animation animFlipInPrevious;
	private Animation animFlipOutPrevious;

	private static final int SELECT_FILE = 1;
	private static final int SELECT_FOLDER_AND_WRITE = 2;

	public static final String FILE_OPEN_TO_DECRYPT = "FILE_OPEN_TO_DECRYPT";
	private String selectedFilePath;

	public static final String MESSAGE_TO_DECRYPT = "MESSAGE_TO_DECRYPT";
	public static final String MESSAGE_NUMBER = "MESSAGE_NUMBER";
	
	private String outputMessage;

	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.decrypt);

		Intent launcher = getIntent();		

		selected = (TextView)findViewById(R.id.de_flipperTitle);
		viewflipper = (ViewFlipper)findViewById(R.id.de_viewFlipper);
		updateSelectedLabel();
		animFlipInNext = AnimationUtils.loadAnimation(this, R.anim.flipinnext);
		animFlipOutNext = AnimationUtils.loadAnimation(this, R.anim.flipoutnext);
		animFlipInPrevious = AnimationUtils.loadAnimation(this, R.anim.flipinprevious);
		animFlipOutPrevious = AnimationUtils.loadAnimation(this, R.anim.flipoutprevious);
		
		String message = launcher.getStringExtra(MESSAGE_TO_DECRYPT);
		String number = launcher.getStringExtra(MESSAGE_NUMBER);
		if(!TextUtils.isEmpty(message) && !TextUtils.isEmpty(number)){
			Log.d("D","normal sms");
			((TextView) findViewById(R.id.de_inputText)).setText(message);
			decryptAndVerifySign();
			//String pkey = ContactManager.getPublicKeyFromPhone(getApplicationContext(), number);
		}

		String path = launcher.getStringExtra(FILE_OPEN_TO_DECRYPT);
		if(!TextUtils.isEmpty(path)){
			Log.d("D","open file");
			viewflipper.setDisplayedChild(viewflipper.indexOfChild(findViewById(R.id.de_view_browse)));
			updateSelectedLabel();
			selectedFilePath = path;
			((TextView) findViewById(R.id.de_textview_selectedFile)).setText("Selected file: "+ selectedFilePath);
		}
		
		if(!TextUtils.isEmpty(path) && !TextUtils.isEmpty(number)){
			Log.d("D","file sms");
			decryptAndVerifySign();
		}

	}

	public void onClick(View v){
		switch (v.getId()) {
		case R.id.de_button_next:
			viewflipper.setInAnimation(animFlipInNext);
			viewflipper.setOutAnimation(animFlipOutNext);
			viewflipper.showNext();
			updateSelectedLabel();
			break;
		case R.id.de_button_previous:
			viewflipper.setInAnimation(animFlipInPrevious);
			viewflipper.setOutAnimation(animFlipOutPrevious);
			viewflipper.showPrevious();
			updateSelectedLabel();
			break;
		case R.id.de_button_browse:
			String[] formatFilter ={"enc","txt"};
			Intent selectFile = new Intent(DecryptActivity.this, FileExplorerActivity.class);
			selectFile.putExtra(FileExplorerActivity.START_PATH, "/sdcard/ForAp");
			selectFile.putExtra(FileExplorerActivity.FORMAT_FILTER,formatFilter);
			selectFile.putExtra(FileExplorerActivity.SELECT_ONLY_FILE_READ, true);
			startActivityForResult(selectFile, SELECT_FILE);
			break;
		case R.id.de_button_decrypt:
			decrypt();
			break;
		case R.id.de_button_verifySign:
			selectPublicKeyOfContact();//verifySign();
			break;
		case R.id.de_button_decryptAndVerifySign:
			decryptAndVerifySign();
			break;
		default:
			Toast.makeText(getApplicationContext(), "This button isn't implemented", Toast.LENGTH_SHORT).show();
			break;
		}
	}



	private byte[] getData() throws IOException{
		byte[] data = null;
		if(viewflipper.getCurrentView().getId() == R.id.de_view_message){
			data = ((TextView)findViewById(R.id.de_inputText)).getText().toString().getBytes();
		}
		if((viewflipper.getCurrentView().getId() == R.id.de_view_browse)){
			if(!TextUtils.isEmpty(selectedFilePath)){
				data = CryptoUtil.getBytesFromFile(new File(selectedFilePath));
			}else{
				Toast.makeText(getApplicationContext(), "Error no file was selected!", Toast.LENGTH_SHORT).show();
			}
		}
		return data;
	}

	private void decrypt() {
		byte[] in = null;
		ForAp app  = ForAp.getInstance();
		try {
			in = getData();
			if(PatternUtil.matchPGPBlock(new String(in), PatternUtil.PGP_MESSAGE)){
				Log.i("DE","1");
				byte[] decrypted = PGPEncryption.decryptWithPGPKey(in, app.getPrivateKey().getBytes(), app.getPassword().toCharArray());
				((TextView)findViewById(R.id.de_output)).setText(new String(decrypted));
				Log.i("DE","1");
				outputMessage = new String(decrypted);
				showOutputMethod();
			} else {
				Toast.makeText(getApplicationContext(), "Wrong Format: This message isn't encrypted.", Toast.LENGTH_LONG).show();
			}
		} catch (Exception e) {
			Toast.makeText(getApplicationContext(), "Decrypt Error: " + e.toString(), Toast.LENGTH_LONG).show();
		}

		/*if(usePassphrase){
			byte[] in = null;
			if(viewflipper.getCurrentView().getId() == R.id.de_view_message){
				try {
					in = ((TextView)findViewById(R.id.de_inputText)).getText().toString().getBytes();
					String key = "Iris";
					byte[] decrypted = PGPEncryption.decrypt(in, key.toCharArray());
					((TextView)findViewById(R.id.de_output)).setText(new String(decrypted));
					outputMessage = new String(decrypted);
					showOutputMethod();
				} catch (Exception e) {
					e.printStackTrace();
					((TextView)findViewById(R.id.de_output)).setText("Encrypt Error:"+ e.toString());
				}
			}
			if((viewflipper.getCurrentView().getId() == R.id.de_view_browse)){
				if(!TextUtils.isEmpty(selectedFilePath)){
					try {
						in = Util.getBytesFromFile(new File(selectedFilePath));
						String key = "Iris";
						byte[] decrypted = PGPEncryption.decrypt(in, key.toCharArray());
						((TextView)findViewById(R.id.de_output)).setText(new String(decrypted));
						outputMessage = new String(decrypted);
						showOutputMethod();
					} catch (Exception e) {
						e.printStackTrace();
						((TextView)findViewById(R.id.de_output)).setText("Encrypt Error:"+ e.toString());
					}
				}else{
					Toast.makeText(getApplicationContext(), "Error no file was selected!", Toast.LENGTH_SHORT).show();
				}
			}else{
				//TODO decrypt with private key
			}
		}*/

	}

	private void verifySign(String pubKey) {
		byte[] in = null;
		try {
			in = getData();
			if(PatternUtil.matchPGPBlock(new String(in), PatternUtil.PGP_SIGNED_MESSAGE)){
				ByteArrayOutputStream out = new ByteArrayOutputStream();
				if(PGPDigitalSignature.verifySignedData(in, pubKey.getBytes(),out)){
					((TextView)findViewById(R.id.de_output)).setText(new String(out.toByteArray()));
					outputMessage = new String(out.toByteArray());
					showVerifySignResult(true);
				}else{
					((TextView)findViewById(R.id.de_output)).setText("Verification FAILED");
					showVerifySignResult(false);
				}
			} else {
				Toast.makeText(getApplicationContext(), "Wrong Format: This message isn't signed.", Toast.LENGTH_LONG).show();
			}

		} catch (IOException e) {
			Toast.makeText(getApplicationContext(), "Verify sign error: " + e.toString(), Toast.LENGTH_LONG).show();
		} catch (NoSuchProviderException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SignatureException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (PGPException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	private void decryptAndVerifySign() {
		byte[] in = null;
		ForAp app  = ForAp.getInstance();
		try {
			in = getData();

			if(PatternUtil.matchPGPBlock(new String(in), PatternUtil.PGP_MESSAGE)){
				byte[] decrypted = PGPEncryption.decryptWithPGPKey(in, app.getPrivateKey().getBytes(), app.getPassword().toCharArray());
				((TextView)findViewById(R.id.de_output)).setText(new String(decrypted));

				if(PatternUtil.matchPGPBlock(new String(decrypted), PatternUtil.PGP_SIGNED_MESSAGE)){
					selectPublicKeyOfContact();
				} else {
					Toast.makeText(getApplicationContext(), "This message is not signed.", Toast.LENGTH_LONG).show();
				}
				outputMessage = new String(decrypted);
				showOutputMethod();
			} else {
				Toast.makeText(getApplicationContext(), "Wrong Format: This message isn't encrypted.", Toast.LENGTH_LONG).show();
			}
		} catch (Exception e) {
			Toast.makeText(getApplicationContext(), "Decrypt Error: " + e.toString(), Toast.LENGTH_LONG).show();
		}

	}


	private void showOutputMethod() {
		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		builder.setTitle("Select output method");
		builder.setIcon(R.drawable.icon);
		builder.setItems(outputMethods, new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog, int index) {
				selectedOutput = index;
				processOutput(selectedOutput);
			}
		});
		AlertDialog alert = builder.create();
		alert.show();
	}


	private final CharSequence[] outputMethods = {"Save to File", "Show in dialog" , "Copy to clipboard"};
	private int selectedOutput;

	private void processOutput(int selectedOutput) {
		switch(selectedOutput){
		case 0://Save to file
			String state = Environment.getExternalStorageState();
			if (Environment.MEDIA_MOUNTED.equals(state)) {
				File sdCard = Environment.getExternalStorageDirectory();
				final File dir = new File (sdCard.getAbsolutePath() + "/ForAp");
				if(!dir.exists())
					dir.mkdirs();
				Intent selectFile = new Intent(DecryptActivity.this, FileExplorerActivity.class);
				selectFile.putExtra(FileExplorerActivity.START_PATH, "/sdcard/forap");
				selectFile.putExtra(FileExplorerActivity.SELECT_ONLY_FOLDER_WRITE, true);
				startActivityForResult(selectFile, SELECT_FOLDER_AND_WRITE);
			}else{
				Toast.makeText(getApplicationContext(), "Error, no (writable) sdcard is mounted!", Toast.LENGTH_SHORT).show();
			}
			break;
		case 1:
			//Show in dialog
			AlertDialog.Builder alert = new AlertDialog.Builder(this);   
			alert.setTitle("Decryption:");
			alert.setMessage(outputMessage);  
			alert.setPositiveButton("Save", new DialogInterface.OnClickListener() {  
				public void onClick(DialogInterface dialog, int whichButton) {
					String state = Environment.getExternalStorageState();
					if (Environment.MEDIA_MOUNTED.equals(state)) {
						File sdCard = Environment.getExternalStorageDirectory();
						final File dir = new File (sdCard.getAbsolutePath() + "/ForAp");
						if(!dir.exists())
							dir.mkdirs();
						Intent selectFile = new Intent(DecryptActivity.this, FileExplorerActivity.class);
						selectFile.putExtra(FileExplorerActivity.START_PATH, "/sdcard/forap");
						selectFile.putExtra(FileExplorerActivity.SELECT_ONLY_FOLDER_WRITE, true);
						startActivityForResult(selectFile, SELECT_FOLDER_AND_WRITE);
					}else{
						Toast.makeText(getApplicationContext(), "Error, no (writable) sdcard is mounted!", Toast.LENGTH_SHORT).show();
					}
				}  
			});  
			alert.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
				public void onClick(DialogInterface dialog, int which) {
					return;   
				}
			});
			alert.show();
		case 2:
			//Copy to clipboard
			ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
			clipboard.setText(outputMessage);
			break;
		}
	}

	private void showVerifySignResult(final boolean verified){
		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		builder.setCancelable(false);

		if(verified){
			builder.setTitle("Successful signature verification");
			builder.setIcon(R.drawable.ic_green_tick);
		} else {
			builder.setTitle("Signature FAILED verification");
			builder.setIcon(R.drawable.ic_red_cross);
		}
		builder.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog, int id) {
				dialog.dismiss();
				if(verified){
					showOutputMethod();
				}
			}
		});
		AlertDialog alert = builder.create();
		alert.show();
	}

	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		if (requestCode == SELECT_FILE) {
			if (resultCode == Activity.RESULT_OK) {				 
				selectedFilePath = data.getStringExtra(FileExplorerActivity.RESULT_PATH);
				((TextView) findViewById(R.id.de_textview_selectedFile)).setText("Selected file: "+ selectedFilePath);
			} else if (resultCode == Activity.RESULT_CANCELED) {
				((TextView) findViewById(R.id.de_textview_selectedFile)).setText("Selected file: "+ "Error selecting a file");
			}
		}
		if (requestCode == SELECT_FOLDER_AND_WRITE) {
			if (resultCode == Activity.RESULT_OK) {				 
				final String folderPath = data.getStringExtra(FileExplorerActivity.RESULT_PATH);
				AlertDialog.Builder alert = new AlertDialog.Builder(this);   
				alert.setTitle("Saving to sd card");
				if(viewflipper.getCurrentView().getId() == R.id.de_view_message){
					alert.setMessage("Enter Filename");  

					// Set an EditText view to get user input   
					final EditText input = new EditText(this); 
					alert.setView(input);

					alert.setPositiveButton("Ok", new DialogInterface.OnClickListener() {  
						public void onClick(DialogInterface dialog, int whichButton) {
							FileOutputStream fos;
							try {
								File file = new File(folderPath, input.getText().toString()+".txt");
								FileOutputStream f = new FileOutputStream(file,false);
								f.write(outputMessage.getBytes());
								f.close();
								Toast.makeText(getApplicationContext(), "File was saved succesfully", Toast.LENGTH_SHORT).show();
							} catch (FileNotFoundException e) {
								Toast.makeText(getApplicationContext(), "Error while saving the file!", Toast.LENGTH_SHORT).show();
								e.printStackTrace();
							}	catch (IOException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}					                
						}  
					});
				}

				if(viewflipper.getCurrentView().getId() == R.id.de_view_browse){
					String[] splitted = selectedFilePath.split("/");
					final String fName =  splitted[splitted.length-1].replace(".enc", "");
					alert.setMessage("Filename: "+ fName); 

					alert.setPositiveButton("Ok", new DialogInterface.OnClickListener() {  
						public void onClick(DialogInterface dialog, int whichButton) {
							FileOutputStream fos;
							try {
								File file = new File(folderPath, fName);
								FileOutputStream f = new FileOutputStream(file,false);
								f.write(outputMessage.getBytes());
								f.close();
								Toast.makeText(getApplicationContext(), "File was saved succesfully", Toast.LENGTH_SHORT).show();
							} catch (FileNotFoundException e) {
								Toast.makeText(getApplicationContext(), "Error while saving the file!", Toast.LENGTH_SHORT).show();
								e.printStackTrace();
							}	catch (IOException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}					                
						}  
					});

				}

				alert.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {

					public void onClick(DialogInterface dialog, int which) {
						Toast.makeText(getApplicationContext(), "File was not saved!", Toast.LENGTH_SHORT).show();
						return;   
					}
				});
				alert.show();
			} else if (resultCode == Activity.RESULT_CANCELED) {
				((TextView) findViewById(R.id.en_textview_selectedFile)).setText("Error selecting a folder");
			}
		}

	}

	private void updateSelectedLabel() {
		switch (viewflipper.getCurrentView().getId()) {
		case R.id.de_view_message: {
			selected.setText("Message");
			break;
		}
		case R.id.de_view_browse: {
			selected.setText("File");
			break;
		}
		default: {
			break;
		}
		}
	}

	private String ownKeyDisplay =  "Own Public Key";
	
	private void selectPublicKeyOfContact() {
		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		final ForapAdapter adapter = new ForapAdapter(this,this, false, true);
		adapter.addPersonalRow();
		
		String[] contactList = adapter.getDisplayNames();

		/*String[] extendedContactList = new String[contactList.length+1];
		extendedContactList[0] = ownKeyDisplay;
		System.arraycopy(contactList, 0, extendedContactList, 1, contactList.length);*/

		final String[] displayList = contactList;//extendedContactList;

		builder.setSingleChoiceItems(contactList, 0,	new DialogInterface.OnClickListener() {
			@Override
			public void onClick(DialogInterface arg0, int arg1) {					
			}
		});

		builder.setTitle("Select contact to verify signature")
		.setPositiveButton("Select", new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog, int whichButton) {
				String pubKey = "";
				ListView list = ((AlertDialog)dialog).getListView();
				//addPublicKey(adapter.getContactId(list.getCheckedItemPosition()),path);
				String selection = displayList[list.getCheckedItemPosition()];
				if(adapter.isPersonalRow(list.getCheckedItemPosition())){
					pubKey = ForAp.getInstance().getPublicKey();
					Log.i("PUBKEY", "own ");
				}else{
					String globalId = adapter.getForapGlobalId(list.getCheckedItemPosition());
					RawContact rc = ContactManager.getRawContactInfo(getApplicationContext(), globalId);
					//integrity check
					String status = "";
					ContactsIntegrityDb db = new ContactsIntegrityDb(DecryptActivity.this);
					db.open();
					ContactIntegrityElement cie = db.getContactIntegrity(globalId);
					db.close();
					if(!cie.verifyIntegrity(rc)){
						showErrorBox("Public key integrity test failed! This false public key is now deleted.");
						ContactManager.deleteContact(getApplicationContext(), globalId);
					}
					status = "Public key integrity verified.";
					Toast.makeText(getApplicationContext(), status, Toast.LENGTH_LONG).show();
					
					pubKey = rc.getPublicKeyBlock();
					
					Log.i("PUBKEY", rc.getBestName() );
				}
				verifySign(pubKey);
			}
		})
		.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog, int whichButton) {
				dialog.cancel();
			}
		})
		.create();
		builder.show();
	}
	
	private void showErrorBox(String title){
		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		builder.setCancelable(false);
		builder.setTitle(title);
		builder.setIcon(R.drawable.ic_red_cross);
		builder.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog, int id) {
				dialog.dismiss();
			}
		});
		AlertDialog alert = builder.create();
		alert.show();
	}
}

